﻿// ———————————————————————–
// <copyright file="SitRep.cs" company="The MITRE Corporation">
//    Copyright (c) 2010 The MITRE Corporation. All rights reserved.
//    Licensed under the Apache License, Version 2.0 (the "License");
//    you may not use this file except in compliance with the License.
//    You may obtain a copy of the License at
//    http://www.apache.org/licenses/LICENSE-2.0
//    Unless required by applicable law or agreed to in writing, software
//    distributed under the License is distributed on an "AS IS" BASIS,
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//    See the License for the specific language governing permissions and
//    limitations under the License.
// </copyright>
// ———————————————————————–
/////////////////////////////////////////////////////////////////////////////////////////////////
// SitRep.cs - root element for the Situation Reporting Messaging Standard
// Project: EDXLSharp- EDXLSitRepLib
//
// Language:    C#, .NET 4.0
// Platform:    Windows 7, VS 2010
// Author:      Don McGarry MITRE Rome

/////////////////////////////////////////////////////////////////////////////////////////////////

// Copyright (c) 2010 The MITRE Corporation. All rights reserved.
//
// NOTICE
// This software was produced for the U. S. Government
// under Contract No. FA8721-09-C-0001, and is
// subject to the Rights in Noncommercial Computer Software
// and Noncommercial Computer Software Documentation Clause
// (DFARS) 252.227-7014 (JUN 1995)

using System;
using System.Collections.Generic;
using System.IO;
using System.ServiceModel.Syndication;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Schema;
using EDXLSharp.EDXLSitRepLib.Schemas;
using System.Xml.Serialization;
using System.Linq;
using System.Text;
//using urn.oasis.names.tc.emergency.edxl.ct.Item1.Item0;
//using urn.oasis.names.tc.emergency.EDXL.SitRep.Item1.Item0;

namespace EDXLSharp.EDXLSitRepLib
{
  /// <summary>
  /// SitRep root element Class
  /// </summary>
  [Serializable]
  public partial class SitRep : IContentObject, IGeoRSS
  {
    /// <summary>
    /// Class Variable To Store Concatenated Schema Validation Errors
    /// </summary>
    private static string schemaErrorString = System.String.Empty;

    /// <summary>
    /// Class Variable To Store Number of Schema Validation Errors
    /// </summary>
    private static int schemaErrors = 0;

    /// <summary>
    /// Top-level SitRep object from the compiled schemas
    /// </summary>
    private Schemas.SitRep sr;

    /// <summary>
    /// Initializes a new instance of the SitRep class
    /// Sets the top level element from the compiled schema
    /// </summary>
    /// <param name="root">Root Element to parse</param>
    public SitRep(XElement root)
    {
        XmlSerializer serializer = new XmlSerializer(typeof(Schemas.SitRep));
        this.sr = (Schemas.SitRep) serializer.Deserialize(root.CreateReader());
    }

    /// <summary>
    /// Initializes a new instance of the SitRep class
    /// Sets the top level element from the compiled schema
    /// </summary>
    public SitRep()
    {
      this.sr = new Schemas.SitRep();
    }

    #region Public Accessors

    /// <summary>
    /// Gets or sets
    /// The ForTimePeriod Element
    /// </summary>
    public TimePeriodType ForTimePeriod
    {
      get { return this.sr.ForTimePeriod; }
      set { this.sr.ForTimePeriod = value; }
    }

    /// <summary>
    /// Gets or sets
    /// Report Contained Within This SitRep Message
    /// </summary>
    public IReport Report
    {
      get { return this.sr.Report; }
      set { this.sr.Report = value; }
    }

    /// <summary>
    /// Gets or sets
    /// The name, number or other identifier of the incident to which the current message refers,
    /// that has been assigned to the incident by an authorized agency based on current guidance,
    /// as the incident number may vary by jurisdiction and profession (e.g. law enforcement vs. Fire).
    /// The incident number may be a computer aided dispatch number, an accounting number,
    /// a disaster declaration number, or a combination of the state, unit/agency, and dispatch system number.
    /// </summary>
    public IList<string> IncidentID
    {
      get { return this.sr.IncidentID; }
      set { this.sr.IncidentID = value.Select(x => x).ToArray(); }
    }

    /// <summary>
    /// Gets or sets
    /// Time and Method of next contact or report planned by the 
    /// “Prepared by” for the purpose of providing updated or additional information.
    /// </summary>
    public DateTime NextContact
    {
      get { return this.sr.NextContact; }
      set { this.sr.NextContact = value; }
    }

    /// <summary>
    /// Gets or sets
    /// Textual needs, expectations or description of what the “Prepared by”
    /// is planning to do next 
    /// </summary>
    public string ActionPlan
    {
      get { return this.sr.ActionPlan; }
      set { this.sr.ActionPlan = value; }
    }

    /// <summary>
    /// Gets or sets
    /// The importance and necessity of the SitRep message.
    /// Use SitRep.CreateValueKey() for simplicity
    /// </summary>
    public UrgencyType Urgency
    {
      get { return this.sr.Urgency; }
      set { this.sr.Urgency = value; }
    }

    /// <summary>
    /// Gets or sets
    /// the incident lifecycle phase
    /// Use SitRep.CreateValueKey() for simplicity
    /// </summary>
    public IList<IncidentLifecycleType> IncidentLifecyclePhase
    {
      get { return this.sr.IncidentLifecyclePhase; }
      set { this.sr.IncidentLifecyclePhase = value.Select(x => x).ToArray(); }
    }

    /// <summary>
    /// Gets or sets
    /// This indicates the current version of the SitRep report being submitted
    /// Use SitRep.CreateValueKey() for simplicity
    /// </summary>
    public ReportVersionType ReportVersion
    {
      get { return this.sr.ReportVersion; }
      set { this.sr.ReportVersion = value; }
    }

    /// <summary>
    /// Gets or sets
    /// the Report purpose
    /// </summary>
    public string ReportPurpose
    {
      get { return this.sr.ReportPurpose; }
      set { this.sr.ReportPurpose = value; }
    }

    /// <summary>
    /// Gets or sets
    /// </summary>
    public string ReportTitle
    {
      get { return this.sr.ReportTitle; }
      set { this.sr.ReportTitle = value; }
    }

    /// <summary>
    /// Gets or sets
    /// </summary>
    public string ReportNumber
    {
      get { return this.sr.ReportNumber; }
      set { this.sr.ReportNumber = value; }
    }

    /// <summary>
    /// Gets or sets
    /// Message ID of the Message That Preceded This One
    /// </summary>
    public IList<string> PrecedingMessageID
    {
      get { return this.sr.PrecedingMessageID; }
      set { this.sr.PrecedingMessageID = value.Select(x => x).ToArray(); }
    }

    /// <summary>
    /// Gets or sets
    /// Message ID of the Message That This Message Was Originally Developed From
    /// </summary>
    public string OriginatingMessageID
    {
      get { return this.sr.OriginatingMessageID; }
      set { this.sr.OriginatingMessageID = value; }
    }

    /// <summary>
    /// Gets or sets
    /// Unique ID for this message
    /// </summary>
    public string MessageID
    {
      get { return this.sr.MessageID; }
      set { this.sr.MessageID = value; }
    }

    /// <summary>
    /// Gets or sets
    /// Location of the Observation
    /// </summary>
    public EDXLLocationType ReportLocation
    {
      get { return this.sr.ReportingLocation; }
      set { this.sr.ReportingLocation = value; }
    }

    /// <summary>
    /// Gets or sets
    /// </summary>
    public PersonTimePairType AuthorizedBy
    {
      get { return this.sr.AuthorizedBy; }
      set { this.sr.AuthorizedBy = value; }
    }

    /// <summary>
    /// Gets or sets
    /// </summary>
    public PersonTimePairType PreparedBy
    {
      get { return this.sr.PreparedBy; }
      set { this.sr.PreparedBy = value; }
    }

    /// <summary>
    /// Gets or sets
    /// </summary>
    public ValueKeyType ReportConfidence
    {
      get { return this.sr.ReportConfidence; }
      set { this.sr.ReportConfidence = value; }
    }

    /// <summary>
    /// Gets or sets
    /// </summary>
    public SeverityType Severity
    {
      get { return this.sr.Severity; }
      set { this.sr.Severity = value; }
    }

    #endregion

    #region Public Member Functions

    /// <summary>
    /// Static Method to Create a new ValueKey Instance
    /// </summary>
    /// <param name="uri">ValueKeyURI URI</param>
    /// <param name="value">Value associated with Key</param>
    /// <returns>Populated ValueKey Object</returns>
    public static ValueKeyType CreateValueKey(string uri, string value)
    {
      return new ValueKeyType { ValueListURI = uri, Value = value };
    }

    /// <summary>
    /// Static Method to Create a new ValueList Instance
    /// </summary>
    /// <param name="uri">ValueListURI URI</param>
    /// <param name="values">Values associated with ListURI</param>
    /// <returns>Populated ValueList Object</returns>
    public static ValueListType CreateValueList(string uri, string[] values)
    {
        return new ValueListType { ValueListURI = uri, Value = values };
    }

    /// <summary>
    /// Override of IContentObject Interface Function
    /// </summary>
    /// <param name="ckw">ValueList Object for Content Keywords</param>
    /// <returns>String to Process ckw Value From</returns>
    public string SetContentKeywords(ValueList ckw)
    {
      ckw.ValueListURN = EDXLConstants.ContentKeywordListName;
      ckw.Value.Add("EDXL-SitRep");
      /*
    if (this.messageReportType != null)
    {
      ckw.Value.Add("EDXL-SitRep " + this.messageReportType.ToString());
      return "EDXL-SitRep " + this.messageReportType.ToString();
    }
       * */

      return "EDXL-SitRep";
    }

    #endregion

    #region Utility Function

    /// <summary>
    /// Writes This Object to an Existing XML Document
    /// </summary>
    /// <param name="xwriter">Pointer to the XMLWriter Writing the Document</param>
    public void WriteXML(XmlWriter xwriter)
    {
      xwriter.WriteRaw(this.sr.ToString());
    }

    /// <summary>
    /// Reads an XML From An Existing DOM
    /// </summary>
    /// <param name="rootnode">Node Containing the root Object</param>
    public void ReadXML(XmlNode rootnode)
    {
      throw new NotImplementedException("ReadXML is not implemented");
    }

    /// <summary>
    /// Converts This IComposableMessage Into A Geo-RSS SyndicationItem
    /// </summary>
    /// <param name="myitems">Pointer to a Valid List of Syndication Items to Populate</param>
    /// <param name="linkUID">The ContentObject Guid To Link To</param>
    public void ToGeoRSS(List<System.ServiceModel.Syndication.SyndicationItem> myitems, Guid linkUID)
    {
      SyndicationItem myitem = new SyndicationItem();

      string primaryAuthor;
      if (this.sr.PreparedBy.PersonDetails.PersonName.Length > 0 )
      {
        primaryAuthor = this.sr.PreparedBy.PersonDetails.PersonName[0].Value;
      }
      else
      {
        primaryAuthor = "Unknown Author";
      }

      myitem.Authors.Add(new SyndicationPerson("admin@icnet.mitre.org", primaryAuthor, System.String.Empty));
      myitem.Categories.Add(new SyndicationCategory(EDXLConstants.SitRepSyndicationCategoryName, EDXLConstants.SyndicationCategoryScheme, EDXLConstants.SyndicationCategoryLabel));
      GeoOASISWhereLib.GeoOASISWhere geoWhere = new GeoOASISWhereLib.GeoOASISWhere();
      XmlNode reportingLocationAsXmlNode;


      StringBuilder locationBuilder = new StringBuilder();      
      using (XmlWriter xmlWriter = XmlWriter.Create(locationBuilder))
      {
          XmlSerializer serializer = new XmlSerializer(typeof(EDXLLocationType));
          serializer.Serialize(xmlWriter, this.sr.ReportingLocation);
      }
      string locationXml = locationBuilder.ToString();

      using (XmlReader xmlReader = XmlReader.Create(new StringReader( locationXml ) ))
      {
        XmlDocument xmlDoc = new XmlDocument();
        xmlDoc.Load(xmlReader);
        reportingLocationAsXmlNode = xmlDoc;
      }

      if (reportingLocationAsXmlNode.FirstChild.FirstChild != null)
      {
          geoWhere.ReadXML(reportingLocationAsXmlNode.FirstChild.FirstChild);
          geoWhere.ToGeoRSS(myitem);
      }

      myitem.Title = new TextSyndicationContent(this.IncidentID.First() + " (EDXL-SitRep - " + this.Report.GetType().Name + " )");
      TextSyndicationContent content = new TextSyndicationContent("Information: " + "\nImmediate Needs: ");
      myitem.Content = content;
      myitem.Copyright = new TextSyndicationContent(EDXLConstants.SyndicationCopyright);
      myitem.LastUpdatedTime = this.sr.ForTimePeriod.FromDateTime;
      myitem.Links.Add(new SyndicationLink(new Uri(EDXLConstants.SyndicationLinkBaseURI + "SitRep/" + linkUID.ToString())));
      myitem.PublishDate = DateTime.Now;
      myitem.Summary = new TextSyndicationContent("MessageID: " + this.sr.MessageID);
      myitems.Add(myitem);
    }

    
    /// <summary>
    /// Validates The Current DE Object Against The XSD Schema File
    /// </summary>
    /// <param name="xmldata">XML String To Parse and Validate</param>
    public void ValidateToSchema(string xmldata)
    {
      schemaErrors = 0;
      XmlReader vr;
      XmlReaderSettings xs = new XmlReaderSettings();
      XmlSchemaSet coll = new XmlSchemaSet();
      StringReader xsdsr = new StringReader(EDXLSharp.EDXLSitRepLib.Properties.Resources.SitRep_Schema_DRAFT_v9);

      StringReader xmlsr = new StringReader(xmldata);
      XmlReader xsdread = XmlReader.Create(xsdsr);
      coll.Add(EDXLConstants.EDXLSitRep10Namespace, xsdread);
      StringReader edxlcommontypesread = new StringReader(EDXLSharp.EDXLSitRepLib.Properties.Resources.edxl_ct_v1_0_wd05);
      XmlReader edxlcommonxsd = XmlReader.Create(edxlcommontypesread);
      coll.Add(EDXLConstants.EDXLCommonTypes10Namespace, edxlcommonxsd);
      StringReader geooasisread = new StringReader(EDXLSharp.EDXLSitRepLib.Properties.Resources.edxl_gsf_v1_0);
      XmlReader geooasisxsd = XmlReader.Create(geooasisread);
      coll.Add(EDXLConstants.GSFNameSpace, geooasisxsd);
      StringReader gmlread = new StringReader(EDXLSharp.EDXLSitRepLib.Properties.Resources.edxl_gsf_base);
      XmlReader gmlxsd = XmlReader.Create(gmlread);
      coll.Add(EDXLConstants.GML32Namespace, gmlxsd);
      StringReader defaultvaluelistread = new StringReader(EDXLSharp.EDXLSitRepLib.Properties.Resources.SitRep_DefaultValueLists_v9);
      XmlReader defaultvlxsd = XmlReader.Create(defaultvaluelistread);
      coll.Add(EDXLConstants.EDXLCommonTypes10Namespace, defaultvlxsd);
      StringReader commontypesread = new StringReader(EDXLSharp.EDXLSitRepLib.Properties.Resources.CommonTypes);
      XmlReader commonxsd = XmlReader.Create(commontypesread);
      coll.Add(EDXLConstants.CIQEDXLCommonTypesNamespace, commonxsd);
      StringReader xalread = new StringReader(EDXLSharp.EDXLSitRepLib.Properties.Resources.edxl_xAL);
      XmlReader xalxsd = XmlReader.Create(xalread);
      coll.Add(EDXLConstants.XALEDXL10Namespace, xalxsd);
      StringReader xaltypesread = new StringReader(EDXLSharp.EDXLSitRepLib.Properties.Resources.xAL_types);
      XmlReader xaltypesxsd = XmlReader.Create(xaltypesread);
      coll.Add(EDXLConstants.XALEDXL10Namespace, xaltypesxsd);
      StringReader xlink2read = new StringReader(EDXLSharp.EDXLSitRepLib.Properties.Resources.xlink_2003_12_31);
      XmlReader xlink2xsd = XmlReader.Create(xlink2read);
      coll.Add("http://www.w3.org/1999/xlink", xlink2xsd);
      StringReader xnlread = new StringReader(EDXLSharp.EDXLSitRepLib.Properties.Resources.edxl_xNL);
      XmlReader xnlxsd = XmlReader.Create(xnlread);
      coll.Add(EDXLConstants.XNLEDXL10Namespace, xnlxsd);
      StringReader xnltypesread = new StringReader(EDXLSharp.EDXLSitRepLib.Properties.Resources.xNL_types);
      XmlReader xnltypesxsd = XmlReader.Create(xnltypesread);
      coll.Add(EDXLConstants.XNLEDXL10Namespace, xnltypesxsd);
      StringReader xpilread = new StringReader(EDXLSharp.EDXLSitRepLib.Properties.Resources.edxl_xPIL);
      XmlReader xpilxsd = XmlReader.Create(xpilread);
      coll.Add(EDXLConstants.XPILEDXL10Namespace, xpilxsd);
      StringReader xpiltypesread = new StringReader(EDXLSharp.EDXLSitRepLib.Properties.Resources.xPIL_types);
      XmlReader xpiltypesxsd = XmlReader.Create(xpiltypesread);
      coll.Add(EDXLConstants.XPILEDXL10Namespace, xpiltypesxsd);
      xs.ValidationType = ValidationType.Schema;
      xs.ValidationFlags |= XmlSchemaValidationFlags.ProcessInlineSchema;
      xs.ValidationFlags |= XmlSchemaValidationFlags.ReportValidationWarnings;
      xs.ValidationEventHandler += new ValidationEventHandler(SchemaErrorCallback);
      vr = XmlReader.Create(xmlsr, xs);
      while (vr.Read())
      {
      }

      vr.Close();
      xmlsr.Close();
      xsdread.Close();
      xsdsr.Close();
      if (schemaErrors > 0)
      {
        throw new ArgumentException("Schema Validation Error: " + schemaErrorString);
      }

      /*
      XmlDocument asset = new XmlDocument();

      // look at base of project instead of in the bin/debug or similar
      DirectoryInfo dir = new DirectoryInfo(AppDomain.CurrentDomain.BaseDirectory);
      string schemaPath = "SitRep_Schema_DRAFT_v5.xsd";
      XmlDocument doc = new XmlDocument();
      doc.LoadXml(xmldata);
      XmlNodeReader nodeReader = new XmlNodeReader(doc);
      XmlReaderSettings settings = new XmlReaderSettings();
      settings.ValidationType = ValidationType.Schema;
      settings.ValidationFlags |= XmlSchemaValidationFlags.ReportValidationWarnings;
      settings.Schemas.Add("urn:oasis:names:tc:emergency:EDXL:SitRep:1.0", schemaPath);
      settings.ValidationEventHandler += new ValidationEventHandler(SchemaValidationHandler);

      ////Create a validating reader that wraps the XmlNodeReader object.
      XmlReader reader = XmlReader.Create(nodeReader, settings);
      ////Parse the XML file.
      while (reader.Read())
      {
      }

      return;
       * */
    }

    /// <summary>
    /// Validates This Message element For Required Values and Conformance
    /// </summary>
    public void Validate()
    {
      if (System.String.IsNullOrEmpty(this.MessageID))
      {
        throw new ArgumentNullException("Message ID is required and can't be null or empty!");
      }

      if (this.ForTimePeriod == null)
      {
        throw new ArgumentNullException("Time Period is required and can't be null!");
      }
    }

    /// <summary>
    /// Static Method to handle schema validation events
    /// </summary>
    /// <param name="sender">Object Firing Event</param>
    /// <param name="args">Validation Event Arguments</param>
    internal static void SchemaValidationHandler(object sender, ValidationEventArgs args)
    {
      if (args.Severity == XmlSeverityType.Warning)
      {
        Console.Write("WARNING: ");
      }
      else if (args.Severity == XmlSeverityType.Error)
      {
        Console.Write("ERROR: ");
      }

      Console.WriteLine(args.Message);
      throw new XmlException("XML Error: " + args.Severity + "  :  " + args.Message);
    }

    #endregion

    #region Private Member Functions

    /// <summary>
    /// Callback Function For Schema Errors
    /// </summary>
    /// <param name="sender">Which object fired this event callback</param>
    /// <param name="args">Validation Event Arguments</param>
    private static void SchemaErrorCallback(object sender, ValidationEventArgs args)
    {
      if (args.Severity == XmlSeverityType.Error)
      {
        schemaErrorString = schemaErrorString + args.Message + "\r\n";
        schemaErrors++;
      }
    }

    #endregion
  }
}
